package gitlabapi

import (
	"encoding/json"
	"fmt"
	"github.com/sirupsen/logrus"
	"gitlab-sync/config"
	"gitlab-sync/util"
	"strconv"
	"strings"
)

type Project struct {
	Id                            int    `json:"id"`
	Name                          string `json:"name"`
	Url                           string `json:"http_url_to_repo"`
	Description                   string `json:"description"`
	Shared_runners_enabled        bool   `json:"shared_runners_enabled"`
	Issues_access_level           string `json:"issues_access_level"`
	Repository_access_level       string `json:"repository_access_level"`
	Merge_requests_access_level   string `json:"merge_requests_access_level"`
	Forking_access_level          string `json:"forking_access_level"`
	Wiki_access_level             string `json:"wiki_access_level"`
	Builds_access_level           string `json:"builds_access_level"`
	Snippets_access_level         string `json:"snippets_access_level"`
	Pages_access_level            string `json:"pages_access_level"`
	Lfs_enabled                   bool   `json:"lfs_enabled"`
	Auto_cancel_pending_pipelines string `json:"auto_cancel_pending_pipelines"`

	Namespace struct {
		Name string `json:"name"`
		Path string `json:"path"`
	}

	Statistics struct{
		Storage_size int64 `json:"storage_size"`
	}
}

//大项目
type BigProject struct {
	Id             int    `json:"id"`
	Name           string `json:"name"`
	Url            string `json:"http_url_to_repo"`
	Description    string `json:"description"`
	RepositorySize string `json:"size"`
}

//MB
const MB = 1024 * 1024

//KB
const KB = 1024

func CreateProjects(search string, prePage string, page int) {
	logrus.Print("开始同步projects")
	for page > 0 {
		url := fmt.Sprintf(config.GetConfig().Gitlab.OriginRemoteUrl+config.GetConfig().Gitlab.ProjectsApi+"?per_page=%s&order_by=name&sort=desc&statistics=true&private_token=%s&page=%d", prePage, config.GetConfig().OriginGitlab.PrivateToken, page)
		if search != "all" {
			logrus.Printf("获取[%s]的信息", search)
			url = fmt.Sprintf(url+"&search=%s", search)
		} else {
			logrus.Printf("查询第[%d]页的projects信息:", page)
		}
		body := util.Get(url, config.GetConfig().Log.RequestLog)
		var projects []Project
		err := json.Unmarshal(body, &projects)
		if err != nil {
			logrus.Error("Error:", err)
			return
		}
		if len(projects) == 0 {
			logrus.Print("--------------[end]---------------")
			break
		}
		CreateProjectsByList(projects, true)
		page++
		if search != "all" {
			page = 0
		}
		logrus.Print("同步projects结束")
	}
}

func CreateProjectsByList(projects []Project, isNeedCreateGroup bool) {
	for _, project := range projects {
		logrus.Printf("开始同步project:%s", project.Name)
		project2, isExsit := GetProjectByName(project.Name)
		if !isNil(project2) && project2.Id != 0 && isExsit {
			logrus.Warn("目标仓库存在相同的项目，请先删除目标仓库的项目再重新迁移！")
			continue
		}
		if validProjectIsExcluded(project) {
			continue
		}
		if isNeedCreateGroup {
			//先创建一个分组
			CreateGroups(project.Namespace.Path)
		}
		//开始迁移git代码
		result := execGitMove(project)
		if result {
			//获取目标仓库的项目的信息
			newProject,_ := GetProjectByName(project.Name)
			//创建项目成员
			CreateMembers(project.Id, newProject.Id, false)
		}
		logrus.Printf("同步project:%s结束", project.Name)
	}
}

// 查询组下的所有projects
func GetProjectsByGroupId(group Group) []Project {
	logrus.Printf("查询组[%s]的所有projects", group.Name)
	var page = 1
	var result []Project
	for page > 0 {
		//循环获取分组，直到没有数据
		url := fmt.Sprintf(config.GetConfig().Gitlab.OriginRemoteUrl+config.GetConfig().Gitlab.GroupsApi+"/%d/projects?private_token="+config.GetConfig().OriginGitlab.PrivateToken+"&page=%d", group.Id, page)
		body := util.Get(url, config.GetConfig().Log.RequestLog)
		var projects []Project
		err := json.Unmarshal(body, &projects)

		if err != nil {
			logrus.Error("Error:", err)
			return nil
		}
		if len(projects) == 0 {
			logrus.Println("当前页无数据，查询结束")
			break
		}
		result = append(result, projects...)
		page++
	}
	logrus.Printf("查询结果：%d条数据", len(result))
	return result
}

func validProjectIsExcluded(project Project) bool {
	var isExcluded = false
	if project.Name != "" {
		excluded := strings.Split(config.GetConfig().Gitlab.SkipProject, ",")
		for _, e := range excluded {
			if e == project.Name {
				isExcluded = true
				break
			}
		}
	}
	//校验文件大小
	if !isExcluded{
		if project.Statistics.Storage_size < MB {
			logrus.Printf("计算文件大小:%dKB", project.Statistics.Storage_size/KB)
		} else {
			storageSize := project.Statistics.Storage_size / MB;
			logrus.Printf("计算文件大小:%dMB", storageSize)
			fileSize, _ := strconv.ParseInt(config.GetConfig().Gitlab.AutoSkipSize, 10, 64)
			if fileSize<storageSize {
				logrus.Warn("文件超出设定的大小，自动跳过")
				bigProject := BigProject{Id: project.Id, Name: project.Name, Url: project.Url, Description: project.Description, RepositorySize: fmt.Sprintf("%dMB", storageSize)}
				bytes, _ := json.Marshal(bigProject)
				util.RecordBigProjectLog(string(bytes))
				isExcluded=true
			}
		}
	}
	logrus.Printf("是否需要跳过,验证结果：%v", isExcluded)
	return isExcluded
}

// 通过项目名称获取新工程上的项目信息
func GetProjectByName(name string) (project Project,isExsit bool) {
	logrus.Printf("根据projectname[%s]获取目标仓库的信息", name)
	//循环获取分组，直到没有数据
	url := fmt.Sprintf(config.GetConfig().Gitlab.TargetRemoteUrl + config.GetConfig().Gitlab.ProjectsApi + "?private_token=" + config.GetConfig().TargetGitlab.PrivateToken + "&search=" + name)
	body := util.Get(url, config.GetConfig().Log.RequestLog)
	var result []Project
	err := json.Unmarshal(body, &result)
	if err != nil {
		logrus.Error("Error:", err)
		return
	}
	if len(result) == 0 {
		return
	} else {
		isExsit=false
		for _,e := range result {
			if e.Name==name{
				isExsit=true
				break
			}
		}
		return result[0],isExsit
	}
}

// 执行迁移
func execGitMove(project Project) (result bool) {
	logrus.Printf("projectName:%s,url:%s", project.Name, project.Url)
	logrus.Print("开始拉取...")
	isok := util.PullGit(project.Name, project.Url)
	if isok {
		logrus.Print("拉取完成")
	}
	//校验文件大小
	//if !validateFileSize(project) {
	//	logrus.Println("------------结束---------------")
	//	return
	//}
	pushUrl := strings.Replace(project.Url, config.GetConfig().Gitlab.OriginRemoteUrl, config.GetConfig().Gitlab.TargetRemoteUrl+"/"+config.GetConfig().TargetGitlab.ParentGroup, -1)
	logrus.Print("开始推送到目标仓库...")
	logrus.Print("目标仓库地址：", pushUrl)
	isok = util.PushGit(project.Name, project.Description, pushUrl)
	if isok {
		logrus.Print("推送完成")
		editProject(project)
		if config.GetConfig().Gitlab.RemoveLocal {
			util.RemoveFile(config.GetConfig().Gitlab.DownLocalPath + project.Name)
		}
	} else {
		logrus.Warn("推送失败，请检查仓库权限")
	}
	return isok
}

//校验文件大小
func validateFileSize(project Project) bool {
	folderPath := config.GetConfig().Gitlab.DownLocalPath + project.Name
	size, err := util.GetFolderSize(folderPath)
	if err != nil {
		logrus.Error(err)
		return false
	}
	if size < MB {
		logrus.Printf("计算文件大小:%dKB", size/KB)
	} else {
		size = size / MB;
		logrus.Printf("计算文件大小:%dMB", size)
		fileSize, _ := strconv.ParseInt(config.GetConfig().Gitlab.AutoSkipSize, 10, 64)
		if size > fileSize {
			logrus.Warn("文件超出设定的大小，自动跳过")
			bigProject := BigProject{Id: project.Id, Name: project.Name, Url: project.Url, Description: project.Description, RepositorySize: fmt.Sprintf("%dMB", size)}
			bytes, _ := json.Marshal(bigProject)
			util.RecordBigProjectLog(string(bytes))
			return false
		}
	}
	return true
}

// 更新项目信息
func editProject(project Project) {
	logrus.Println("更新项目描述信息")
	project2,_ := GetProjectByName(project.Name)
	project.Id = project2.Id
	url := fmt.Sprintf(config.GetConfig().Gitlab.TargetRemoteUrl+config.GetConfig().Gitlab.ProjectsApi+"/%d", project.Id)
	bytesData, _ := json.Marshal(project)
	body := util.Put(url, bytesData, config.GetConfig().TargetGitlab.PrivateToken, config.GetConfig().Log.RequestLog)
	var result Project
	err := json.Unmarshal(body, &result)
	if err != nil {
		logrus.Error("Error:", err)
		return
	} else {
		logrus.Println("更新完成")
	}
}
